package models

import (
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/EDDYCJY/go-gin-example/pkg/setting"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

var db *gorm.DB

type Model struct {
	ID int `gorm:"primary_key" json:"id"`
}

func GetDB() *gorm.DB {
	return db
}

// Setup initializes the database instance
func Setup() {
	var err error
	db, err = gorm.Open(mysql.Open(fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",
		setting.DatabaseSetting.User,
		setting.DatabaseSetting.Password,
		setting.DatabaseSetting.Host,
		setting.DatabaseSetting.Name)), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   setting.DatabaseSetting.TablePrefix, // 为所有表添加前缀
			SingularTable: true,
		}})

	if err != nil {
		log.Fatalf("models.Setup err: %v", err)
	}

	if setting.AppSetting.Env == "test" {
		db = db.Debug()
	}

	sqlDB, err := db.DB()
	if err != nil {
		log.Fatalf("failed to connect to database: %v", err)
	}

	sqlDB.SetMaxIdleConns(setting.DatabaseSetting.MaxIdleConns)
	sqlDB.SetMaxOpenConns(setting.DatabaseSetting.MaxOpenConns)
}

type NullType byte

const (
	_ NullType = iota
	// IsNull the same as `is null`
	IsNull
	// IsNotNull the same as `is not null`
	IsNotNull
)

// sql build where
func WhereBuild(where map[string]interface{}) (whereSQL string, vals []interface{}, err error) {
	// 提取键并排序
	keys := make([]string, 0, len(where))
	for k := range where {
		keys = append(keys, k)
	}
	sort.Strings(keys) // 按字母顺序排序
	for _, k := range keys {
		v := where[k]
		ks := strings.Split(k, " ")
		if len(ks) > 2 {
			return "", nil, fmt.Errorf("Error in query condition: %s. ", k)
		}

		if whereSQL != "" {
			whereSQL += " AND "
		}

		strings.Join(ks, ",")
		switch len(ks) {
		case 1:
			//fmt.Println(reflect.TypeOf(v))
			if ks[0] == "exp" {
				whereSQL += "(" + v.(string) + ")"
			} else {
				switch v := v.(type) {
				case NullType:
					if v == IsNotNull {
						whereSQL += fmt.Sprint(k, " IS NOT NULL")
					} else {
						whereSQL += fmt.Sprint(k, " IS NULL")
					}
				default:
					whereSQL += fmt.Sprint(k, "=?")
					vals = append(vals, v)
				}
			}
		case 2:
			k = ks[0]
			switch ks[1] {
			case "=":
				kss := strings.Split(k, "|")
				if len(kss) > 1 {
					var text string
					for _, v10 := range kss {
						text += fmt.Sprint(v10, " = ? or ")
						vals = append(vals, v)
					}
					text = strings.TrimRight(text, "or ")
					whereSQL += "(" + text + ")"
				} else {
					whereSQL += fmt.Sprint(k, " = ?")
					vals = append(vals, v)
				}
			case ">":
				whereSQL += fmt.Sprint(k, ">?")
				vals = append(vals, v)
			case ">=":
				whereSQL += fmt.Sprint(k, ">=?")
				vals = append(vals, v)
			case "<":
				whereSQL += fmt.Sprint(k, "<?")
				vals = append(vals, v)
			case "<=":
				whereSQL += fmt.Sprint(k, "<=?")
				vals = append(vals, v)
			case "!=":
				whereSQL += fmt.Sprint(k, "!=?")
				vals = append(vals, v)
			case "<>":
				whereSQL += fmt.Sprint(k, "!=?")
				vals = append(vals, v)
			case "in":
				whereSQL += fmt.Sprint(k, " in (?)")
				vals = append(vals, v)
			case "notin":
				whereSQL += fmt.Sprint(k, " not in (?)")
				vals = append(vals, v)
			case "like":
				kss := strings.Split(k, "|")
				if len(kss) > 1 {
					var text string
					for _, v10 := range kss {
						text += fmt.Sprint(v10, " like ? or ")
						vals = append(vals, v)
					}
					text = "*" + text
					text = strings.Trim(text, "or ")
					text = strings.Trim(text, "*")
					whereSQL += "(" + text + ")"
				} else {
					whereSQL += fmt.Sprint(k, " like ?")
					vals = append(vals, v)
				}
			case "notlike":
				whereSQL += fmt.Sprint(k, " not like ?")
				vals = append(vals, v)
			case "between":
				whereSQL += fmt.Sprint(k, " between ? and ?")
				data := v.([]string)
				vals = append(vals, data[0])
				vals = append(vals, data[1])
			case "or":
				list := v.(map[string]interface{})
				var text string
				for k1, v1 := range list {
					ks1 := strings.Split(k1, " ")
					strings.Join(ks1, ",")
					switch len(ks1) {
					case 1:
						text += k1 + "= ? or "
						vals = append(vals, v1)

					case 2:
						k = ks1[0]
						switch ks1[1] {
						case "like":
							data := v1.([]string)
							for _, v := range data {
								text += k + " like ? or "
								vals = append(vals, v)
							}
						}
					}
				}
				text = strings.Trim(text, "or ")
				whereSQL += "(" + text + ")"
			}
		}
	}
	return
}
